package anivis.ui.map;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Paint;

import anivis.AniVisApp;
import anivis.base.DataAvailableNotification;
import anivis.base.Notification;
import anivis.base.RestrictionNotification;
import anivis.db.QueryLayer;
import anivis.db.QueryLayer.Restriction;
import anivis.db.QueryLayer.RestrictionType;
import anivis.layers.ObservationLayer;
import anivis.ui.Controller;

import com.bbn.openmap.Layer;
import com.bbn.openmap.event.ProjectionEvent;
import com.bbn.openmap.omGraphics.OMCircle;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMLine;
import com.bbn.openmap.omGraphics.OMPoint;
import com.bbn.openmap.proj.Projection;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.tuple.TableTuple;

import com.bbn.openmap.LatLonPoint;

public class AnimalLayer extends Layer implements Observer {

	/**
     * A list of graphics to be painted on the map.
     */
    private ObservationLayer layer;
    private OMGraphicList omgraphics;
    
    private boolean waitsForData = true;

    /**
     * Construct a default route layer. Initializes omgraphics to a
     * new OMGraphicList, and invokes createGraphics to create the
     * canned list of routes.
     */
    public AnimalLayer() {
        omgraphics = new OMGraphicList();
        // register
        layer = new ObservationLayer("Avian Network",true,true);
        HashSet<RestrictionType> r = new HashSet<RestrictionType>();
        r.add(RestrictionType.Longitude);
        r.add(RestrictionType.Latitude);
        r.add(RestrictionType.TimeWindow);
        r.add(RestrictionType.SpatialAggregation);
        layer.setRestrictionTypes(r);
        AniVisApp.getApplication().getController().addObserver(this);
        createGraphics(omgraphics);
    }
    
    /**
     * Clears and then fills the given OMGraphicList. Creates three
     * lines for display on the map.
     * 
     * @param graphics The OMGraphicList to clear and populate
     * @return the graphics list, after being cleared and filled
     */
    public synchronized OMGraphicList createGraphics(OMGraphicList graphics) {
        System.out.println("Repainting animal layer.");
        omgraphics.clear();
        
        // fill color
        Color fillColor = new Color(0.85f, 0.18f, 0.15f, 0.05f);
        
        Table table = layer.getTable();
        synchronized(table) {
            System.out.println("AnimalLayer got: " + table.getRowCount());
            int latitudeIndex = table.getColumnNumber("latitude");
            int longitudeIndex = table.getColumnNumber("longitude");
            int aggregationIndex = table.getColumnNumber("aggregation");
            for (int i = 1; table.isValidRow(i); i++) {
                Tuple tuple = table.getTuple(i);
            	OMPoint omPoint = new OMPoint(tuple.getFloat(latitudeIndex), tuple.getFloat(longitudeIndex), 1);
                //OMCircle omCircle = new OMCircle(tuple.getFloat(latitudeIndex), tuple.getFloat(longitudeIndex), 0.25f);
                //omCircle.setLinePaint(linePaint);
                //omCircle.setLinePaint()(new Color(1.0f, 0, 0));            	
            	omPoint.setLineColor(fillColor);
                //omCircle.setFillPaint(fillColor);
            	//graphics.addOMGraphic(omCircle);
            	graphics.addOMGraphic(omPoint);
            }
        }
        
        return graphics;
    }

    //----------------------------------------------------------------------
    // Layer overrides
    //----------------------------------------------------------------------

    /**
     * Renders the graphics list. It is important to make this routine
     * as fast as possible since it is called frequently by Swing, and
     * the User Interface blocks while painting is done.
     */
    public void paint(java.awt.Graphics g) {
        omgraphics.render(g);
    }

    //----------------------------------------------------------------------
    // ProjectionListener interface implementation
    //----------------------------------------------------------------------

    /**
     * Handler for <code>ProjectionEvent</code>s. This function is
     * invoked when the <code>MapBean</code> projection changes. The
     * graphics are reprojected and then the Layer is repainted.
     * <p>
     * 
     * @param e the projection event
     */
    public void projectionChanged(ProjectionEvent e) {
        System.out.println(e.getProjection().getUpperLeft() + " " + e.getProjection().getLowerRight());
        // TODO: call the controller and set the restrictions based on the current visualization of the map (corners)
        // call the controller
        Controller controller = AniVisApp.getApplication().getController();
        LatLonPoint upperLeft = e.getProjection().getUpperLeft();
        LatLonPoint lowerRight = e.getProjection().getLowerRight();
        QueryLayer.RangeRestriction longitudeRestriction = new QueryLayer.RangeRestriction(upperLeft.getLongitude(), lowerRight.getLongitude(), RestrictionType.Longitude);
        QueryLayer.RangeRestriction latitudeRestriction = new QueryLayer.RangeRestriction(lowerRight.getLatitude(), upperLeft.getLatitude(), RestrictionType.Latitude);
        QueryLayer.Aggregation aggregation = new QueryLayer.Aggregation(1, QueryLayer.RestrictionType.SpatialAggregation);
        controller.setRestriction(longitudeRestriction);
        controller.setRestriction(latitudeRestriction);
        controller.setRestriction(aggregation);
        
        // prepare data
        controller.prepareData();
        
        omgraphics.project(e.getProjection(), true);
        repaint();
    }

    public synchronized void update(Observable sender, Object message) 
    {
      System.out.println("AnimalLayer.update " + message);
      Notification notification = (Notification) message;
      
      if(notification.isRestrictionNotification())
      {
         RestrictionNotification n = (RestrictionNotification) notification;
         if( layer.getRestrictionTypes().contains( n.getRestriction().getType() ) )
         { waitsForData = true; }
      }
      else
        if(notification.isDataAvaialableNotification()) 
        { 
          DataAvailableNotification n = (DataAvailableNotification) notification;
          Set<RestrictionType> rT = n.getRestrictionTypes();
          if(waitsForData/* && rT.contains(layer.getRestrictionTypes())*/)
          {  /*get data*/
            anivis.ui.Controller c = (Controller) sender;
            
            // prepare the data
            layer.getData(c);
            
            // draw the points
            createGraphics(omgraphics);
            
            // repaint our graphics
            repaint();
            
            waitsForData = false;
          }
        }  
        
    }


}
